﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="EqualsShould.cs" company="SaltFx">
//   Copyright (c) SaltFx - All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace SaltFx.Tests.PersistableObject
{
	using System;
	using System.Collections.Generic;
	using System.Diagnostics;
	using System.Linq;

	using NUnit.Framework;

	/// <summary>
	/// The PersistableObject equality tests.
	/// </summary>
	public class EqualsShould
	{
		#region Public Methods

		/// <summary>
		/// Ensures that two persistable objects with the same surrogate ids are considered equal.
		/// </summary>
		[Test]
		public void ReturnTrueGivenIdenticalSurrogateIdsAndNonMatchingKeys()
		{
			var sid = Guid.NewGuid();

			var left = new KeyedTestPersistableObject("FOO", "PEANUT") { Sid = sid };
			var right = new KeyedTestPersistableObject("BAR", "WALNUT") { Sid = sid };

			Assert.IsTrue(left.Equals(right));
		}

		/// <summary>
		/// Ensures that two unkeyed persistable objects with the same surrogate ids are considered equal.
		/// </summary>
		[Test]
		public void ReturnTrueGivenIdenticalSurrogateIdsAndNoKeys()
		{
			var sid = Guid.NewGuid();

			var left = new UnkeyedTestPersistableObject("FOO", "PEANUT") { Sid = sid };
			var right = new UnkeyedTestPersistableObject("BAR", "WALNUT") { Sid = sid };

			Assert.IsTrue(left.Equals(right));
		}

		/// <summary>
		/// Ensures that two unkeyed persistable objects with the same surrogate ids are considered equal.
		/// </summary>
		[Test]
		public void NotBlowUpGivenNoSurrogateIdsAndNoKeys()
		{
			var left = new UnkeyedTestPersistableObject("FOO", "PEANUT");
			var right = new UnkeyedTestPersistableObject("BAR", "WALNUT");

			var testList = new List<UnkeyedTestPersistableObject> { left, right };

			foreach (var testListItem in testList)
			{
				Assert.IsTrue(testList.Contains(testListItem));	
			}
		}

		/// <summary>
		/// Ensures that two persistable objects with the same keys and without surrogate are considered equal.
		/// </summary>
		[Test]
		public void ReturnTrueGivenMatchingKeysAndWithoutSurrogateIds()
		{
			var left = new KeyedTestPersistableObject("MATCHINGCODE", "PEANUT");
			var right = new KeyedTestPersistableObject("MATCHINGCODE", "WALNUT");

			Assert.IsTrue(left.Equals(right));
		}

		/// <summary>
		/// Ensures that two persistable objects with different keys and without surrogate are NOT considered equal.
		/// </summary>
		[Test]
		public void ReturnFalseGivenNonMatchingKeysAndWithoutSurrogateIds()
		{
			var left = new KeyedTestPersistableObject("FOO", "PEANUT");
			var right = new KeyedTestPersistableObject("BAR", "WALNUT");

			Assert.IsFalse(left.Equals(right));
		}

		/// <summary>
		/// Ensures that a copy of a persistable object is equal to the original.
		/// </summary>
		[Test]
		public void ReturnTrueGivenReferenceCopy()
		{
			var original = new KeyedTestPersistableObject("FOO", "HAZELNUT") { Sid = Guid.NewGuid() };
			var copy = original;

			Assert.IsTrue(copy.Equals(original));
		}

		/// <summary>
		/// Ensures that two persistable objects with different keys and surrogate ids are NOT considered equal.
		/// </summary>
		[Test]
		public void ReturnFalseGivenNonMatchingKeysAndSurrogateIds()
		{
			var left = new KeyedTestPersistableObject("FOO", "PEANUT") { Sid = Guid.NewGuid() };
			var right = new KeyedTestPersistableObject("BAR", "ALMOND") { Sid = Guid.NewGuid() };

			Assert.IsFalse(left.Equals(right));
		}

		/// <summary>
		/// Ensures that two persistable objects with matching keys and non-matching surrogate ids are NOT considered equal.
		/// </summary>
		[Test]
		public void ReturnFalseGivenMatchingKeysAndNonMatchingSurrogateIds()
		{
			var left = new KeyedTestPersistableObject("FOO", "PEANUT") { Sid = Guid.NewGuid() };
			var right = new KeyedTestPersistableObject("FOO", "ALMOND") { Sid = Guid.NewGuid() };

			Assert.IsFalse(left.Equals(right));
		}

		/// <summary>
		/// Ensures that two persistable objects with the same surrogate ids are considered equal.
		/// </summary>
		[Test]
		public void ReturnTrueUsingOperatorGivenIdenticalSurrogateIds()
		{
			var sid = Guid.NewGuid();

			var left = new KeyedTestPersistableObject("FOO", "FILBERT") { Sid = sid };
			var right = new KeyedTestPersistableObject("BAR", "PECAN") { Sid = sid };

			Assert.IsTrue(left == right);
		}

		/// <summary>
		/// Ensures that a copy of a persistable object is equal to the original.
		/// </summary>
		[Test]
		public void ReturnTrueUsingOperatorGivenReferenceCopy()
		{
			var original = new KeyedTestPersistableObject("FOO", "FILBERT") { Sid = Guid.NewGuid() };
			var copy = original;

			Assert.IsTrue(copy == original);
		}

		/// <summary>
		/// Ensures that two persistable objects with different surrogate ids are NOT considered equal.
		/// </summary>
		[Test]
		public void ReturnFalseUsingOperatorGivenNonMatchingSurrogateIds()
		{
			var left = new KeyedTestPersistableObject("FOO", "PEANUT") { Sid = Guid.NewGuid() };
			var right = new KeyedTestPersistableObject("BAR", "WALNUT") { Sid = Guid.NewGuid() };

			Assert.IsFalse(left == right);
		}

		/// <summary>
		/// Ensures the equality operators allow for persistable objects to be the key in dictionaries.
		/// </summary>
		[Test]
		public void AllowForPersistableObjectsWithoutSurrogateIdsAsKeysInDictionaries()
		{
			// Build an object...
			var testObject = new KeyedTestPersistableObject("BOOFY", "BLIMP");

			// Now put those into a dictionary where their type is the key...
			var testDictionary = new Dictionary<KeyedTestPersistableObject, int> { { testObject, 20 } };

			Debug.WriteLine("Dupe Hash 1: " + "BOOFY".GetHashCode());
			Debug.WriteLine("Dupe Hash 2: " + "BOOFY".GetHashCode());
			Debug.WriteLine("Dupe Hash 3: " + "BOOFY".GetHashCode());
			Debug.WriteLine("Dupe Hash 4: " + "BOOFY".GetHashCode());
			Debug.WriteLine("Dupe Hash 5: " + "BOOFY".GetHashCode());

			Debug.WriteLine("-A-------------------------");
			Debug.WriteLine("ToString: " + testObject.ToString());
			Debug.WriteLine("     Key: " + testObject.Key);
			Debug.WriteLine("    Hash: " + testObject.GetHashCode());
			Debug.WriteLine("Raw Hash: " + "BOOFY".GetHashCode());

			foreach (var obj in from obj in testDictionary let testPersistableObject = obj.Key select obj)
			{
				Debug.WriteLine("-N-------------------------");
				Debug.WriteLine("ToString: " + obj.ToString());
				Debug.WriteLine("     Key: " + obj.Key);
				Debug.WriteLine("    Hash: " + obj.GetHashCode());
				Debug.WriteLine("Equals A: " + obj.Equals(testObject));
				Debug.WriteLine("Key Hash: " + obj.Key.GetHashCode());
			}

			Assert.IsTrue(testDictionary.ContainsKey(testObject));
		}

		/// <summary>
		/// Ensures the equality operators allow for persistable objects to be removed from dictionaries.
		/// </summary>
		[Test]
		public void AllowRemovalFromDictionariesViaReference()
		{
			// Create the values we'll need for our test...
			var guidA = Guid.NewGuid();
			var codeA = "BLARG";
			var nameA = "BONES";

			// Build three persistable objects (making sure that one of them uses our saved values)...
			var objectA = new KeyedTestPersistableObject(codeA, nameA) { Sid = guidA };
			var objectB = new KeyedTestPersistableObject("BOOFY", "BLIMP") { Sid = Guid.NewGuid() };
			var objectC = new KeyedTestPersistableObject("BARFY", "BARRY") { Sid = Guid.NewGuid() };

			// Now put those into a dictionary where their type is the key...
			var testDictionary = new Dictionary<KeyedTestPersistableObject, int> { { objectA, 10 }, { objectB, 20 }, { objectC, 30 } };

			// Wipe out the entities just for fun...
			objectA = null;
			objectB = null;
			objectC = null;

			// Heck, go ahead and garbage collect while we're at it...
			GC.Collect();

			// Now build a brand-new object with the same VALUES as one that's in our dictionary...
			var brandNewObject = new KeyedTestPersistableObject(codeA, nameA) { Sid = guidA };

			// Grab the object in our dictionary that has a matching code...
			var fetchedObject = testDictionary.Keys.SingleOrDefault(x => x.Code.Equals(brandNewObject.Code));

			// Make absolutely sure that the object from our dictionary and the one we just build match...
			Assert.IsTrue(brandNewObject.Equals(fetchedObject));

			// Seriously... make sure...
			Assert.IsTrue(brandNewObject == fetchedObject);

			// I mean it... make 100% sure they match...
			Assert.IsTrue(brandNewObject.GetHashCode().Equals(fetchedObject.GetHashCode()));

			// Flip it around even...
			Assert.IsFalse(brandNewObject != fetchedObject);

			// Groovy... now remove the object using the one we pulled out...
			testDictionary.Remove(fetchedObject);

			// Assert (it's gone, right?)
			Assert.IsFalse(testDictionary.ContainsKey(fetchedObject));

			// Are we sure?
			Assert.IsTrue(testDictionary.Count.Equals(2));
		}

		/// <summary>
		/// Ensures the equality operators allow for persistable objects to be removed from dictionaries using equality.
		/// </summary>
		[Test]
		public void AllowRemovalFromDictionariesViaEquality()
		{
			// Create the values we'll need for our test...
			var guidA = Guid.NewGuid();
			var codeA = "BLARG";
			var nameA = "BONES";

			// Build three objects (making sure that one of them uses our saved values)...
			var objectA = new KeyedTestPersistableObject(codeA, nameA) { Sid = guidA };
			var objectB = new KeyedTestPersistableObject("BOOFY", "BLIMP") { Sid = Guid.NewGuid() };
			var objectC = new KeyedTestPersistableObject("BARFY", "BARRY") { Sid = Guid.NewGuid() };

			// Now put those into a dictionary where their type is the key...
			var testDictionary = new Dictionary<KeyedTestPersistableObject, int> { { objectA, 10 }, { objectB, 20 }, { objectC, 30 } };

			// Wipe out the objects just for fun...
			objectA = null;
			objectB = null;
			objectC = null;

			// Heck, go ahead and garbage collect while we're at it...
			GC.Collect();

			// Now build a brand-new object with the same VALUES as one that's in our dictionary...
			var brandNewObject = new KeyedTestPersistableObject(codeA, nameA) { Sid = guidA };

			// Grab the object in our dictionary that has a matching code...
			var fetchedObject = testDictionary.Keys.SingleOrDefault(x => x.Code.Equals(brandNewObject.Code));

			// Make absolutely sure that the object from our dictionary and the one we just build match...
			Assert.IsTrue(brandNewObject.Equals(fetchedObject));

			// Seriously... make sure...
			Assert.IsTrue(brandNewObject == fetchedObject);

			// I mean it... make 100% sure they match...
			Assert.IsTrue(brandNewObject.GetHashCode().Equals(fetchedObject.GetHashCode()));

			// Flip it around even...
			Assert.IsFalse(brandNewObject != fetchedObject);

			// Groovy... now remove the object using the NEW one we built-up that is VALUE identical...
			testDictionary.Remove(brandNewObject);

			// See that? Removed it by *equality* alone!

			// Assert (it's gone, right?)
			Assert.IsFalse(testDictionary.ContainsKey(fetchedObject));

			// Are we sure?
			Assert.IsTrue(testDictionary.Count.Equals(2));
		}

		#endregion
	}
}